package com.tbit.uqbike.client.controller;

import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tbit.common.constant.OperationPlatform;
import com.tbit.common.entity.MachineOpLog;
import com.tbit.common.utils.IpUtil;import com.tbit.uqbike.center.pojo.MachineStatus;
import com.tbit.uqbike.client.business.machine_op_log.service.MachineOpLogService;
import com.tbit.uqbike.client.config.RefundSwitchConfig;
import com.tbit.uqbike.client.constant.*;
import com.tbit.uqbike.client.constant.annotation.PreventDuplication;
import com.tbit.uqbike.client.controller.vo.BorrowPopUp;
import com.tbit.uqbike.client.dao.core.AccountConfigExtDao;
import com.tbit.uqbike.client.dao.core.MachineFunctionDao;
import com.tbit.uqbike.client.dao.log.RefundOrderDao;
import com.tbit.uqbike.client.dao.ter.MachineAttrDao;
import com.tbit.uqbike.client.dto.BorrowUserInfoDTO;
import com.tbit.uqbike.client.exception.BaseException;
import com.tbit.uqbike.client.handler.CheckService;
import com.tbit.uqbike.client.pojo.*;
import com.tbit.uqbike.client.pojo.newEntity.AccountConfigExt;
import com.tbit.uqbike.client.pojo.newEntity.BleHelmet;
import com.tbit.uqbike.client.remote.DataUtils;
import com.tbit.uqbike.client.remote.GatewayRedisCinent;
import com.tbit.uqbike.client.service.*;
import com.tbit.uqbike.client.util.*;
import com.tbit.uqbike.client.util.asc.AesTool;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.LogManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.InputStreamReader;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 设备
 *
 * @author Leon
 * 2017年2月24日 下午5:51:05
 */
@Controller
@RequestMapping("/machine")
@Slf4j
public class MachineController {

    private final static org.apache.logging.log4j.Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
    @Autowired
    private MachineService machineService;
    @Autowired
    private TokenService tokenService;
    @Autowired
    private CalibrateService calibrateService;
    @Autowired
    private UserService userService;
    @Autowired
    private RedisService redisService;
    @Autowired
    private MachineLastUseService machineLastUseService;
    @Autowired
    private AccountConfigService accountConfigService;
    @Autowired
    private AdAccountFeeService adAccountFeeService;
    @Autowired
    private MachineBorrowService machineBorrowService;
    @Autowired
    private RideLogService rideLogService;
    @Autowired
    private ParkMachineService parkMachineService;
    @Autowired
    private OrderLogService orderLogService;
    @Autowired
    private BleHelmetService bleHelmetService;
    @Autowired
    private FinishOrderService finishOrderService;
    @Autowired
    private MachineAttrDao machineEventDao;
    @Autowired
    private CheckService checkService;
    @Autowired
    private TerControlService terControlService;
    @Autowired
    private MachineStatusService machineStatusService;
    @Autowired
    private GeoService geoService;
    @Autowired
    private ForbiddenService forbiddenService;
    @Autowired
    private CacheService cacheService;
    @Autowired
    private MachineFunctionDao machineFunctionDao;
    @Autowired
    private AccountService accountService;
    @Resource
    private RefundSwitchConfig refundSwitchConfig;
    @Resource
    private RefundOrderDao refundOrderDao;
    @Resource
    private MachineOpLogService machineOpLogService;
    @Resource
    private AccountConfigExtDao accountConfigExtDao;

    /**
     * 用户借车
     *
     * @param token    用户token
     * @param userCode 设备编号
     * @param lon      经度
     * @param lat      纬度
     * @param mapType  地图类型
     * @param ble      是否蓝牙
     * @param useWxzff  是否使用支付分 0不使用1使用
     * @param session
     * @param request
     * @param response
     */
    @RequestMapping("/borrowBike")
    @PreventDuplication(expireSeconds = 5)
    public void borrowBike(String token, String userCode, String borrowPosLogId,Double lon, Double lat,
                           Integer mapType, Boolean ble,
                           @RequestParam(defaultValue = "1") Integer useWxzff,
                           HttpSession session, HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<>();

        try {
            /* 校验 */
            Assert.hasLength(userCode, "-30006", "用户码不能为空");
            /**去除空格*/
            userCode = userCode.trim();
            if (ble == null) {
                ble = false;
            }
            if (mapType == null) {
                mapType = SysConstant.GOOGLE;
            }
            if (SystemConstant.borrow_button) {
                throw new BaseException("-2014", "当前使用人数较多，请稍后再试");
            }
            String header = request.getHeader("orderSource");
            Integer orderSource;
            /**默认来源微信小程序*/
            if (header == null) {
                orderSource = 3;
            } else {
                orderSource = (Integer.valueOf(header));
            }
            Machine machine = machineService.getByUserCode(userCode);
            if (machine == null) {
                throw new BaseException("-30005", "该编号不存在");
            }
            //5.0.0优化需求， 根据微信/支付宝小程序的运营开关状态判断是否限制用车
            if (Objects.equals(orderSource,3)){
                // 微信
                AccountConfigExt accountConfigExt = accountConfigExtDao.selectByAccountIdAndKey(machine.getAccountId(), "isSupportWechatProgram");
                if (accountConfigExt != null && !Objects.equals(accountConfigExt.getParamValue(),"1")){
                    throw new BaseException("-210001","微信小程序运营开关已关闭");
                }
            }
            if (Objects.equals(orderSource,4)){
                // 支付宝
                AccountConfigExt accountConfigExt = accountConfigExtDao.selectByAccountIdAndKey(machine.getAccountId(), "isSupportAliProgram");
                if (accountConfigExt != null && !Objects.equals(accountConfigExt.getParamValue(),"1")){
                    throw new BaseException("-210002","暂不支持支付宝小程序借车，请用微信扫码借车");
                }
            }
            User user = tokenService.getUser(token);
            // 判断用户是否存在退款中的状态
            String borrowLimit = refundSwitchConfig.getBorrowLimit();
            if (Objects.equals(borrowLimit,RefundSwitchConfig.REFUND_PENDING_BORROW_LIMIT)){
                int count = refundOrderDao.countPendingOrder(user.getUserId());
                if (count > 0){
                    throw new BaseException("存在退款中的订单，请稍后再试");
                }
            }
            Assert.notNull(lon, "-2001", "经度不能为空");
            Assert.notNull(lat, "-2002", "纬度不能为空");
            User userdb = userService.getByUserId(user.getUserId());
            //防止用户登录后被删除情况
            if (userdb == null) {
                throw new BaseException("账号已被注销，请重新登录");
            }
            //欠费判断
            checkArrears(userdb, map, machine);

            /**找车时长逻辑处理,放入mq*/
            String addTime = redisService.get(RedisConstant.USER_LOGIN_SIGN + user.getUserId());
            Integer machineId = machine.getMachineId();
            if (addTime != null) {
                ParkMachine parkMachine = parkMachineService.getByMachineId(machineId);
                Integer parkPointId = null;
                if (parkMachine != null) {
                    parkPointId = parkMachine.getParkPointId();
                }
                String software = request.getHeader("software");
                String appWare = request.getHeader("appWare");
                String phoneType = request.getHeader("phoneType");
                String way = request.getHeader("way");
                DataUtils
                        .addFind(machine, user.getUserId(), parkPointId, addTime, software, appWare, phoneType, way, orderSource);
            }

            //校验是否停运
            Boolean flag = machineService.checkServiceSwitch(user.getAccountId(), machine.getAccountId());
            if (!flag){
                throw new BaseException("运营商已关闭");
            }

            String machineNO = machine.getMachineNO();
            MachineStatus machineStatus = machineStatusService.getByMachineNO(machineNO, MapConstant.MAP_GOOGLE);
            /**蓝牙发送开锁指令，检查是否设置了设备离线后禁止发送蓝牙指令，同时检查当前时间和上一次车辆在线时间的间隔是否超过了设置的时间*/
            if (ble && accountConfigService.checkAccountConfigExt(machine.getAccountId(),
                    (Objects.nonNull(machineStatus) ? machineStatus.getBatDt() : null),
                    (Objects.nonNull(machineStatus) ? machineStatus.getMachineType() : null))){
                throw new BaseException("-3056", "蓝牙搜索车辆失败，请靠近车辆后操作，是否重新搜索？");
            }

            AccountConfigExt accountConfig_outOfRange = cacheService.getAccountConfigExtByKey(machineStatus.getAccountId(), AccountConfigExtConstant.OUT_OF_RANGE);
            // 允许运营区外借车开关
            boolean allowOutOfRangeBorrow = Objects.nonNull(accountConfig_outOfRange) && Objects.equals(accountConfig_outOfRange.getParamValue(), "1");
            AccountConfigExt accountConfig_tooFar = cacheService.getAccountConfigExtByKey(machineStatus.getAccountId(), AccountConfigExtConstant.TOO_FAR_BORROW);
            // 允许超远距离借车开关
            boolean allowTooFarBorrow = Objects.nonNull(accountConfig_tooFar) && Objects.equals(accountConfig_tooFar.getParamValue(), "1");
            /**判断车辆是否在电子围栏内*/
            if (MachineStateConstant.MACHINE_TYPE_ELECT_BIKE.equals(machine.getMachineType())) {
                if (machineStatus != null && machineStatus.getLon() != null && machineStatus.getLat() != null) {
                    if (machineStatus.getLon() == null || machineStatus.getLat() == null) {
                        throw new BaseException("-3029", "故障车辆，车辆还没有上线", terControlService, machine, user.getUserId(),
                                UseTimeConstant.OPEATE_BORROW, null, null, null, null);
                    }
                    Boolean result = forbiddenService.checkInForbidden(machine.getUserCode(), new Point(machineStatus.getLon(), machineStatus.getLat()));
                    if (result) {
                        throw new BaseException("-4007", "车辆在禁行区内,请更换车辆");
                    }

                    // 命中超远距离规则 ， 将限制运营区外借车
                    int tooFarFlag = 0;

                    Point point = new Point(machineStatus.getLon(), machineStatus.getLat());
                    AccountConfigExt ext = cacheService.getAccountConfigExtByKey(machine.getAccountId()
                            , AccountConfigExtConstant.ACCOUNTCONFIG_MACHINE_BORROW_RANGE);
                    if (ext != null) {
                        Point point1 = new Point(lon, lat);
                        point1 = calibrateService.calibrate(point1, mapType, SysConstant.ORIGINAL);
                        /***/
                        Double distance = GeoUtil.getPointDistance(point1, point);
                        Integer value = Integer.valueOf(ext.getParamValue());
                        logger.info("借车距离===" + distance + ",设置范围=" + value);
                        if (distance > value && value > 0) {
                            if (!allowTooFarBorrow){
                                throw new BaseException("-20023", "位置距离车辆过远，请更换车辆");
                            }
                            // 超远距离触发借车标识
                            redisService.add(String.format(RedisConstant.TOO_FAR_BORROW_BIKE,machine.getMachineId()),"1",2*60);
                            tooFarFlag = 1;
                        }
                    }
                    Boolean isInArea = geoService.checkInGeo(machine.getAccountId(), new Point(machineStatus.getLon(), machineStatus.getLat()));
                    // 运营区外 ， 在 未打开运营去外借车 或 已命中超远距离借车 的情况下进行限制
                    // 添加超区缓存标识
                    addOutOfRangeCache(isInArea, allowOutOfRangeBorrow, machine);
                    if (!isInArea && (!allowOutOfRangeBorrow || tooFarFlag == 1) ) {
                        throw new BaseException("-3047", "运营区域外禁止借车", terControlService, machine, user.getUserId(),
                                UseTimeConstant.OPEATE_BORROW, null, null, null, null);
                    }
                    /**缓存借车位置*/
                    redisService.add(RedisConstant.MACHINE_BORROW_LOCAL + machineNO, JSON.toJSONString(point), 60);
                } else {
                    throw new BaseException("-3029", "故障车辆，车辆还没有投放");
                }
            }

            /**单车围栏逻辑判断*/
            if (machine.getMachineType().equals(MachineStateConstant.MACHINE_TYPE_BIKE)) {
                Point point = new Point(lon, lat);
                point = calibrateService.calibrate(point, mapType, SysConstant.ORIGINAL);
                Boolean isInArea = geoService.checkInGeo(machine.getAccountId(), point);
                // 添加超区缓存标识
                addOutOfRangeCache(isInArea, allowOutOfRangeBorrow, machine);
                if (!isInArea && !allowOutOfRangeBorrow) {
                    //terControlService.sendErrorMsg(machine,user.getUserId(),UseTimeConstant.OPEATE_BORROW,"-3047",null);
                    throw new BaseException("-3047", "运营区域外禁止借车", terControlService, machine, user.getUserId(),
                            UseTimeConstant.OPEATE_BORROW, null, null, null, null);
                }
            }

            /**电量判断,只判断电单车*/
            if (machineStatus != null && machine.getMachineType().equals(MachineStateConstant.MACHINE_TYPE_ELECT_BIKE)) {
                /**电量是否足够*/
                AccountConfig accountConfig = accountConfigService.getByAccountId(machine.getAccountId());
                if (machineStatus.getSocPercent() <= accountConfig.getBorrowSocLimit()) {
                    /**发送电量不足语音指令*/
                    String serNO = UUID.randomUUID().toString();
                    terControlService.sendControl(serNO, machineNO, ControlTypeConstant.CONTROL_TYPE_VOICE,
                            ControlTypeConstant.CONTROL_TYPE_VOICE_LOWPOWER, "", machine, true, null, null, null);
                    throw new BaseException("-3031", "电量过低，请更换一辆车", terControlService, machine, user.getUserId(),
                            UseTimeConstant.OPEATE_BORROW, null, null, null, null);
                }
            }

            /**生成流水号*/
            String serNO = UUID.randomUUID().toString();

            BorrowBusMoney borrowBusMoney = machineService.borrowBike(serNO, userdb, user.getAccountId(), machine, ble, orderSource, borrowPosLogId,useWxzff);

            if (borrowBusMoney.getDepositMoeny() != null) {
                if (borrowBusMoney.getDepositMoeny() > 0) {
                    /**补交押金*/
                    map.put(ResponseCode.DATA, DoubleUtil.format(borrowBusMoney.getDepositMoeny()));
                    throw new BaseException("-3014", "提交的押金不足借此车，请补足押金再借");
                } else if (borrowBusMoney.getDepositMoeny() <= 0) {
                    /**未提交押金*/
                    map.put(ResponseCode.DATA, -DoubleUtil.format(borrowBusMoney.getDepositMoeny()));
                    throw new BaseException("-3002", "请先提交押金");
                }
            }

            /**要求首充才能骑行*/
            if (borrowBusMoney.getRechargeMoney() != null) {
                map.put(ResponseCode.DATA, DoubleUtil.format(borrowBusMoney.getRechargeMoney()));
                throw new BaseException("-3049", "请先充值再骑行");
            }

            // 骑行最低金额
            if (borrowBusMoney.getMiniMoney() != null) {
                map.put(ResponseCode.DATA, DoubleUtil.format(borrowBusMoney.getMiniMoney()));
                throw new BaseException("-3055", "存在最低账户余额限制");
            }

            /**蓝牙通道，创建订单*/
            if (ble) {
                LogUtil.info("[" + machineNO + "]使用蓝牙通道借车");
                machineService.ter_unlock(serNO);
            }

            /**记录用户发送的流水号*/
            redisService.add("rSerNo" + user.getUserId(), serNO, 60);
            /**记录用户发送的时间*/
            String nowTime = DateTimeUtil.getNowTime();
            redisService.add("rTime" + user.getUserId(), nowTime, 60);
            /**设备最后开锁时间*/
            redisService.add("MACHINELAST" + machineNO, nowTime, 24 * 3600);
			/*if(lon!=null&&lat!=null){
				Point point = new Point(lon,lat);
				redisService.add("bikepos."+machine.getMachineId(),JSON.toJSONString(point),60);
			}*/

			/*Integer tmp=machineService.checkHelmet(machine,user.getUserId());
			if(tmp!=null){
        *//**头盔锁缓存*//*
        redisService.add(RedisConstant.MACHINE_NO_HELMET+user.getUserId(),tmp.toString(),60*60);
      }*/

            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     *  添加超区缓存标识
     * @param isInArea 是否超区
     * @param allowOutOfRangeBorrow 运营区外借车开关
     * @param machine 车辆
     */
    private void addOutOfRangeCache(Boolean isInArea, boolean allowOutOfRangeBorrow, Machine machine) {
        if (!isInArea && allowOutOfRangeBorrow){
            // 运营区外借车标识
            redisService.add(String.format(RedisConstant.OUT_OF_RANGE_BORROW_BIKE, machine.getMachineId()),"1",2*60);
        }
    }

    /**
     * 欠费判断
     * @param user 用户信息
     * @param map 返回结果
     * @param machine 当前借车车辆
     */
    private void checkArrears(User user, Map<String, Object> map, Machine machine) {
        //用户借车车辆之前提示用户金额是否为负的
        Integer userId = user.getUserId();
        /**金额判断*/
        if (user.getMoney() - user.getRideMoney() < 0) {
            map.put(ResponseCode.DATA, user.getMoney() - user.getRideMoney());
            throw new BaseException("-3039", "您的账户已经欠费，请补交欠费金额", terControlService, machine, userId,
                    UseTimeConstant.OPEATE_BORROW, null, null, null, null);
        }
        if (!StringUtils.hasText(user.getIdNO())) {
            return;
        }
        //品牌下同一身份证欠费校验开关
        AccountConfigExt accountConfigExtByKey = cacheService.getAccountConfigExtByKey(machine.getAccountId(), AccountConfigExtConstant.ID_CARD_ARREARS_KEY);
        if (accountConfigExtByKey != null && AccountConfigExtConstant.ID_CARD_ARREARS_OPEN.equals(accountConfigExtByKey.getParamValue())) {
            //借车判断该用户同身份证下的账号是否有欠费
            if (userService.checkArrearsByIdNO(user.getAccountId(), user.getIdNO())) {
                throw new BaseException("同一身份证认证的其他账号有欠款，请补交后再骑行");
            }
        }
    }


    /**
     * 检验是否合法
     *
     * @param token    令牌
     * @param userCode 车辆编号
     * @param type     类型
     * @param money    金额
     * @param lo       经度
     * @param la       纬度
     * @param mapType  地图类型
     * @param session  会话
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/check")
    public void check(String token, String userCode, @RequestParam(defaultValue = "1") Integer type, @RequestParam(defaultValue = "200") Integer money
            , Double lo, Double la, @RequestParam(defaultValue = "2") Integer mapType, HttpSession session,
                      HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            /* 校验 */
            Assert.hasLength(userCode, "-30006", "用户码不能为空");
            Assert.notNull(lo, "-2001", "经度不能为空");
            Assert.notNull(la, "-2002", "纬度不能为空");
            Point point = new Point(lo, la);
            point = calibrateService.calibrate(point, mapType, SysConstant.ORIGINAL);
            User user = tokenService.getUser(token);
            /**type=1为借车校验，=0为还车校验*/
            if (type == 1) {
                /**用户借车车辆之前提示用户金额是否为负的*/
                User userdb = userService.getByUserId(user.getUserId());
                /**金额判断*/
                if (userdb.getMoney() - userdb.getRideMoney() < 0) {
                    map.put(ResponseCode.DATA, userdb.getMoney() - userdb.getRideMoney());
                    throw new BaseException("-3039", "您的账户已经欠费，请补交欠费金额");
                }
                machineService.checkIsOk(user, userCode, point);
            } else {
                Dispatch dispatchDB = new Dispatch(null, money.doubleValue());
                /**是否在区域内*/
                Dispatch dispatch = machineService.areaCheck(user.getUserId(), userCode, point, dispatchDB);
                User userDB = userService.getByUserId(user.getUserId());
                if (dispatch.getMoney() > 0) {
                    map.put("dispachMoney", dispatch);
                }
                if (userDB.getMoney() < dispatchDB.getMoney() + dispatch.getMoney()) {
                    map.put(ResponseCode.DATA, dispatchDB.getMoney() + dispatch.getMoney() - userDB.getMoney());
                    //throw new BaseException("-3004","用户余额不足");
                }

            }
            map.put(ResponseCode.RET, ResponseCode.SUCC);

        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 单车借车,纯蓝牙
     *
     * @param token    令牌
     * @param userCode 车辆编号
     * @param session  会话
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/borrow")
    public void borrow(String token, String userCode, String borrowPosLogId,HttpSession session, HttpServletRequest request,
                       HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            /* 校验 */
            Assert.hasLength(userCode, "-30006", "用户码不能为空");
            User user = tokenService.getUser(token);
            machineService.borrow(user, userCode,borrowPosLogId);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }
        OutputUtil.renderText(request, response, map);
    }

    /**
     * 单车获取蓝牙秘钥
     *
     * @param token    令牌
     * @param key      单车中控编号
     * @param session  会话
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/getBleKey")
    public void getBleKey(String token, String key, HttpSession session, HttpServletRequest request,
                          HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            Assert.notNull(key, "-30006", "关键字不能为空");
            String no = ExcelUtil.getMachineNO("D:\\excel\\machine.xlsx", key);
            System.out.println(no);
            BleExcelPojo bleExcelPojo;
            if (no != null) {
                bleExcelPojo = ExcelUtil.readByMachineNO("D:\\excel\\excel.xlsx", no);
            } else {
                bleExcelPojo = ExcelUtil.readByMachineNO("D:\\excel\\excel.xlsx", key);
            }
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, bleExcelPojo);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }
        OutputUtil.renderText(request, response, map);
    }

    /**
     * 单车还车
     *
     * @param token
     * @param money    扣费金额 单位分
     * @param lo       经度
     * @param la       纬度
     * @param mapType  地图类型
     * @param session  会话
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/remand")
    public void remand(String token, @RequestParam(defaultValue = "200") Integer money, Integer dispatchType, Double lo, Double la, @RequestParam(defaultValue = "2") Integer mapType,
                       HttpSession session, HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            /* 校验 */
            Assert.notNull(lo, "-2001", "经度不能为空");
            Assert.notNull(la, "-2002", "纬度不能为空");
            Point point = new Point(lo, la);
            point = calibrateService.calibrate(point, mapType, SysConstant.ORIGINAL);
            User user = tokenService.getUser(token);
            machineService.remand(user, money, point, dispatchType, false);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }
        OutputUtil.renderText(request, response, map);
    }


    /**
     * 先还车后付费还车
     *
     * @param token        令牌
     * @param ble          是否蓝牙
     * @param lo           经度
     * @param la           纬度
     * @param isBaidu      是百度
     * @param dispatchType 调度类型
     * @param secondType   第二调度类型
     * @param session      会话
     * @param response     响应
     * @ignoreParams request
     */
    @RequestMapping("/firstReturnBikeAfterPay")
    public void firstReturnBikeAfterPay(String token, Boolean ble, Double lo, Double la, Boolean isBaidu, Integer dispatchType, Integer secondType,
                                        HttpSession session, HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            User user = tokenService.getUser(token);
            String lang = request.getHeader("locale");
            if (lang == null) {
                lang = "cn";
            }
            if (ble == null) {
                ble = false;
            }

            /**调度类型*/
            if (dispatchType != null && dispatchType != 3 && dispatchType != 4) {
                dispatchType = null;
            }

            /**蓝牙通道还车，必须上传车辆经纬度判断是否在停车点*/
            Point point = null;
            if (ble) {
                Assert.notNull(lo, "-2001", "经度不能为空");
                Assert.notNull(la, "-2002", "纬度不能为空");

                /*校准百度经纬度为原始经纬度*/
                point = new Point(lo, la);
                if (isBaidu != null && isBaidu) {
                    point = calibrateService.calibrate(point, SysConstant.BAIDU, SysConstant.ORIGINAL);
                }
            } else {
                if (lo != null && la != null) {
                    /*校准百度经纬度为原始经纬度*/
                    point = new Point(lo, la);
                    if (isBaidu != null && isBaidu) {
                        point = calibrateService.calibrate(point, SysConstant.BAIDU, SysConstant.ORIGINAL);
                    }
                }
            }
            String ip = IpUtil.getClientIp(request);
            /**生成流水号*/
            String serNO = UUID.randomUUID().toString();

            Double money = machineService.returnBike(serNO, user.getUserId(), ble, point, dispatchType, false, 1,
                    MapConstant.MAP_GOOGLE, lang, ip, secondType);
            if (money != null && money > 0D) {
                money = DoubleUtil.format(money);
                /**格式精度丢失*/
                if (money == 0) {
                    money = money + 0.01d;
                }
                if (money > 0) {
                    LogUtil.warn("[" + user.getPhone() + "]个人金额不足");
                    map.put(ResponseCode.DATA, DoubleUtil.format(money));
                    throw new BaseException("-3004", "个人账户余额不足，请先缴费");
                }
            }

            /**蓝牙通道，结束订单*/
            if (ble) {
                LogUtil.info("[" + user.getPhone() + "]蓝牙通道还车");
                machineService.ter_lock(serNO);
            }

            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 还车
     *
     * @param token        令牌
     * @param ble          是否蓝牙
     * @param lo           经度
     * @param la           纬度
     * @param mapType      地图类型
     * @param dispatchType 调度类型
     * @param secondType   第二调度类型
     * @param session      会话
     * @param response     响应
     * @ignoreParams request
     */
    @RequestMapping("/returnBike")
    @PreventDuplication(expireSeconds = 5)
    public void returnBike(String token, Boolean ble, Double lo, Double la, Integer mapType, Integer dispatchType, Integer secondType,
                           HttpSession session, HttpServletRequest request, HttpServletResponse response) {

        Map<String, Object> map = new HashMap<String, Object>();
        try {
            User user = tokenService.getUser(token);
            /**默认中文*/
            String lang = Optional.ofNullable((String) request.getHeader("lang")).orElseGet(() -> "cn");
            /**默认来源微信小程序*/
            Integer orderSource = Optional.ofNullable((String) request.getHeader("orderSource")).map(o -> Integer.valueOf(o)).orElseGet(() -> 3);
            /**默认谷歌地图*/
            mapType = Optional.ofNullable(mapType).orElseGet(() -> MapConstant.MAP_GOOGLE);
            /**获取ip*/
            String ip = IpUtil.getClientIp(request);
            /**调度类型不为以下三种，则为空*/
            ble = Optional.ofNullable(ble).orElseGet(() -> false);
            dispatchType = Optional.ofNullable(dispatchType).filter(
                    d -> d.equals(DisPatchConstant.DISPATCH_NOT_IN_AREA)
                            || d.equals(DisPatchConstant.DISPATCH_NO_IN_PARK)
                            || d.equals(DisPatchConstant.DISPATCH_OVERFLOW_PARK)
                            || d.equals(DisPatchConstant.DISPATCH_NO_PARK_AREA)       )
                    .orElseGet(() -> null);
            /**蓝牙通道还车，必须上传车辆经纬度判断是否在停车点*/
            Point point = null;
            if (ble) {
                Assert.notNull(lo, "-2001", "经度不能为空");
                Assert.notNull(la, "-2002", "纬度不能为空");
                /*校准百度经纬度为原始经纬度*/
                point = new Point(lo, la);
                point = calibrateService.calibrate(point, mapType, SysConstant.ORIGINAL);
                log.info("蓝牙通道还车：lo :" + lo + ",la :" + la + "校准后经纬度：" + "lo :" + point.getX() + ",la:" + point.getY());
            }
            if (lo != null && la != null) {
                point = new Point(lo, la);
                point = calibrateService.calibrate(point, mapType, SysConstant.ORIGINAL);
            }

            /**生成流水号*/

            //String serNO= OrderNoUtil.getOrderNO((long) TenpayUtil.buildRandom(6), 1000);
            String serNO = UUID.randomUUID().toString();
            //log.info("用户"+user.getUserId()+"发起还车");
            //  还车调度检查时支持调度还车的1,3,4,14类型时，check值为1
            String check = redisService.get(RedisConstant.USER_RETURN_BIKE + user.getUserId());
            Boolean auto = null != check ? true : false;
            // 还车

            Double money = machineService.returnBike(serNO, user.getUserId(), ble, point, dispatchType, auto, orderSource, mapType, lang, ip, secondType);
            if (money != null && money > 0D) {
                money = DoubleUtil.format(money);
                /**格式精度丢失*/
                if (money == 0) {
                    money = money + 0.01d;
                }
                if (money > 0) {
                    LogUtil.warn("[" + user.getPhone() + "]个人金额不足," + money);
                    map.put(ResponseCode.DATA, money);
                    throw new BaseException("-3004", "个人账户不足支付，请先支付骑行费用");
                }
            }

            /**蓝牙通道，结束订单*/
            if (ble) {
                LogUtil.info("[" + user.getPhone() + "]蓝牙通道还车");
                machineService.ter_lock(serNO);
            }

            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 获取用户骑行费用
     *
     * @param userId   用户id
     * @param online   是否查询实时数据 1实时
     * @param session  会话
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/getFee")
    public void getFee(Integer userId, @RequestParam(defaultValue = "1") Integer online, HttpSession session,
                       HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            User userdb = userService.getByUserId(userId);
            if (userdb == null) {
                throw new BaseException("-20005", "用户不存在");
            }

            RideLog rideLog = machineService.getBorrowingInfo(userdb.getAccountId(), userId, online);

            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, rideLog);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 获取当前借出车辆
     *
     * @param token    令牌
     * @param userCode 车辆编号
     * @param session  会话
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/getInfo")
    public void getInfo(String token, String userCode, HttpSession session, HttpServletRequest request,
                        HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            /**设备*/
            Machine machine = machineService.getByUserCode(userCode);
            if (machine == null) {
                throw new BaseException("-30005", "");
            }
            MachineBorrow machineBorrow = machineBorrowService.getByMachineId(machine.getMachineId());
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, machineBorrow);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 获取单车借车信息
     *
     * @param token    令牌
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/getRemand")
    public void getRemand(String token, String userCode, HttpServletRequest request,
                          HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            User user = tokenService.getUser(token);
            User userdb = userService.getByUserId(user.getUserId());
            if (userdb == null) {
                throw new BaseException("-20005", "用户不存在");
            }

            MachineBorrow machineBorrow = machineBorrowService.getByUserId(user.getUserId());
            RideLog rideLog = null;
            if (machineBorrow != null) {
                Machine machine = machineService.getByMachineId(machineBorrow.getMachineId());
                rideLog = rideLogService.getByOrderNO(machineBorrow.getOrderNO());
                if (rideLog != null) {
                    rideLog.setMachineType(machine.getMachineType());
                }
            }
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, rideLog);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 获取当前借车信息
     *
     * @param token    令牌
     * @param online   是否实时查询 1实时查询
     * @param session  会话
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/getBorrowing")
    public void getBorrowing(String token, @RequestParam(defaultValue = "0") Integer online, HttpSession session,
                             HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            User user = tokenService.getUser(token);
            RideLog rideLog = machineService.getBorrowingInfo(user.getAccountId(), user.getUserId(), online);
            if (rideLog != null) {
                String serNO = redisService.get(RedisConstant.MACHINE_NO_SERNO + rideLog.getUserCode());
                if (serNO != null) {
                    OrderLog orderLog = orderLogService.getByOrderNO(serNO);
                    if (orderLog != null && orderLog.getRspRet() != null && orderLog.getRspRet() == GatewayProtocol.RSP_RESULT_RFID) {
                        rideLog.setErrorCode(ErrorCodeConstant.CODE_RFID);
                    } else if (orderLog != null && orderLog.getRspRet() != null && (orderLog.getRspRet() == GatewayProtocol.RSP_RESULT_WEBCAM || orderLog.getRspRet() == GatewayProtocol.RSP_RESULT_STATUS9)) {
                        rideLog.setErrorCode(ErrorCodeConstant.CODE_WEBCAM);
                    }
                }
                /**兼容借车低电断电功能*/
                String tmp = redisService.get(RedisConstant.MACHINE_BORROW_MAIN + rideLog.getMachineNO());
                map.put("power", tmp);
            } else {
                //用户预约车辆之前提示用户金额是否为负的,这里最好查询一下数据库
                User userdb = userService.getByUserId(user.getUserId());
                if (userdb != null && userdb.getMoney() < 0) {
                    map.put(ResponseCode.RET, ResponseCode.SUCC);
                    map.put("arrearage", userdb.getMoney());
                    OutputUtil.renderText(request, response, map); //输出
                    return; //不执行下面的代码
                }
            }
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, rideLog);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 获取蓝牙连接密钥
     *
     * @param token    令牌
     * @param userCode 车辆编号
     * @param session  会话
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/getBleSecret")
    public void getBleSecret(String token, String userCode, HttpSession session,
                             HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            if (token != null) {
                User user = tokenService.getUser(token);
                LogUtil.info("用户[" + user.getPhone() + "]获取设备[" + userCode + "]蓝牙连接密钥");
            }

            /* 校验 */
            Assert.hasLength(userCode, "-30006", "用户码不能为空");

            Machine machine = machineService.getByUserCode(userCode);
            if (machine == null) {
                throw new BaseException("-30005", "车辆不存在");
            }
            String secret = "";//蓝牙秘钥
            boolean isSpecial = false;//是否是特殊蓝牙秘钥特殊处理

            String machineNO = machine.getMachineNO();
            if (machineNO.startsWith("ECA")) {
                String redisSecret = redisService.get("SPECIAL_BLE_ECA_PRE:" + machineNO);
                if (redisSecret != null) {
                    isSpecial = true;
                    secret = redisSecret;
                }
            }

            /**兼容终端多位编码生成蓝牙的坑,截取后面9位生成蓝牙秘钥*/

            int length = machineNO.length();
            MachineEvent machineEvent = null;
            if (length > 9) {

                //兼容小安设备
                machineEvent = machineEventDao.getMachineEvent(machine.getMachineNO(), "tBTToken");
                machine.setMachineNO(machineNO.substring(length - 9, length));
            }
            if (!isSpecial) {
                secret = AesTool.Genkey(SystemConstant.BLE_KEY, machine.getMachineNO());
            }
            if (machineEvent != null) {
                secret = machineEvent.getEventValue();
            }
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("secret", secret);

            jsonObject.put("machineNO", machineNO);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, jsonObject);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }
        OutputUtil.renderText(request, response, map);
    }

    /**
     * 判断设备是否在线
     *
     * @param token    用户标志
     * @param userCode 用户码
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/isOnline")
    public void isOnline(String token, String userCode, HttpServletRequest request,
                         HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            //数据校验
            Assert.hasLength(userCode, "-30006", "用户码不能为空");
            //终端是否在线
            boolean isOnline = false;
            Machine machine = machineService.getByUserCode(userCode);
            if (machine == null) {
                throw new BaseException("-30005", "车辆不存在");
            }
            //判断终端是否在线
            String lastTerBatteryTime = GatewayRedisCinent
                    .getSecondHash(RedisUtil.getTerHashId(machine.getUserCode()), RedisConstant.REDIS_TER_PKG_DT);
            if (lastTerBatteryTime != null) {
                //判断电池时间和当前时间的时间差
                Date batTime = DateTimeUtil.StringToDateTime(lastTerBatteryTime);
                Date nowTime = new Date();
                if (nowTime.getTime() - batTime.getTime() <= 3 * 60 * 1000) {
                    isOnline = true;
                }
            }
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, isOnline);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        } catch (Exception e) {//捕获其他异常，防止redis空指针异常
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, false);
        }
        OutputUtil.renderText(request, response, map);
    }


    /**
     * 更新车辆最后使用信息
     *
     * @param token    令牌
     * @param userCode 车辆编号
     * @param lo       经度
     * @param la       纬度
     * @param mapType  地图类型
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/lastUse")
    public void lastUse(String token, String userCode, Double lo, Double la, Integer mapType,
                        HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            /* 校验 */
            Assert.hasLength(userCode, "-30006", "用户码不能为空");

            if (mapType == null) {
                mapType = MapConstant.MAP_GOOGLE;
            }

            /**用户借车车辆之前提示用户金额是否为负的*/
            User user = tokenService.getUser(token);
            User userdb = userService.getByUserId(user.getUserId());
            Machine machine = machineService.getByUserCode(userCode);

            Account accountAgent = accountService.getByAdAccountId(machine.getAccountId());
            if (!userdb.getAccountId().equals(accountAgent.getParentId())) {
                throw new BaseException("-30005", "该编号不存在");
            }
            Long borrowPosLogId=null;
            /**更新最后扫码人信息*/
            if (userdb != null && machine != null && lo != null & la != null) {
                borrowPosLogId = machineLastUseService.updateLastInfo(machine.getMachineId(), machine.getMachineNO(), new Point(lo, la), userdb.getUserId(), userdb.getPhone(), mapType);
            }

            map.put(ResponseCode.RET, ResponseCode.SUCC);
            if (Objects.nonNull(borrowPosLogId)) {
                map.put(ResponseCode.DATA, borrowPosLogId.toString());
            }
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 获取预估费用
     *
     * @param time        时间
     * @param mileage     里程
     * @param adAccountId 广告帐户id
     * @param session     会话
     * @param response    响应
     * @ignoreParams request
     */
    @RequestMapping("/getExpectFee")
    public void getExpectFee(Integer time, Double mileage, Integer adAccountId, HttpSession session, AdAccountFee accountFee,
                             HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            /**计算预估费用*/
            /**总费用*/
            Double money = 0D;
            /**查询代理商定义费用*/
            AdAccountFee adAccountFee = adAccountFeeService.getByAccountId(adAccountId, null);
            if (accountFee.getAccountId() != null) {
                adAccountFee = accountFee;
            }
            if (adAccountFee == null) {
                throw new BaseException("-3056", "请先配置计费规则");
            }
            /**免费时长*/
            Integer freeTime = adAccountFee.getFreeTime() == null ? 0 : adAccountFee.getFreeTime();

            /**骑行费用*/
            money = money + MoneyUtil.getRideFee(time, mileage, adAccountFee, freeTime);


            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, money);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 检验是否支持新临时停车语音
     *
     * @param token    令牌
     * @param userCode 车辆编号
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/checkNewParkVoice")
    public void checkNewParkVoice(String token, String userCode, HttpServletRequest request,
                                  HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            /* 校验 */
            Assert.hasLength(userCode, "-30006", "用户码不能为空");
            Boolean result = machineService.checkNewParkVoice(userCode);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, result);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 上传打开app信息
     *
     * @param token       令牌
     * @param adAccountId 区域id
     * @param lon         经度
     * @param lat         纬度
     * @param mapType     地图类型
     * @param response    响应
     * @ignoreParams request
     */
    @RequestMapping("/openApp")
    public void openApp(String token, Integer adAccountId, Double lon, Double lat, @RequestParam(defaultValue = "2") Integer mapType,
                        HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            if (token != null) {
                Assert.notNull(lon, "-2001", "经度不能为空");
                Assert.notNull(lat, "-2002", "纬度不能为空");

                User user = tokenService.getUser(token);
                if (user != null) {
                    /**区域id为空，则取品牌id*/
                    if (adAccountId == null) {
                        adAccountId = user.getAccountId();
                    }
                    Point point = new Point(lon, lat);
                    point = calibrateService.calibrate(point, mapType, SysConstant.ORIGINAL);
                    /**记录打开app时间*/
                    redisService.add(RedisConstant.USER_LOGIN_SIGN + user.getUserId(), DateTimeUtil.getNowTime(), 1800);
                    /**打开位置 数据入库*/
					/*ReturnBikeMsg returnBikeMsg = new ReturnBikeMsg();
					returnBikeMsg.setAccountId(adAccountId);
					returnBikeMsg.setLon(point.getX());
					returnBikeMsg.setLat(point.getY());
					returnBikeMsg.setResult(UseTimeConstant.RESULT_SUCCESS);
					returnBikeMsg.setType(UseTimeConstant.OPEATE_FIND);
					SystemData.bikeMsgList.add(returnBikeMsg);*/
                }
            }

            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 借还车耗时上报
     *
     * @param token    令牌
     * @param type     类型 0 借车 1还车
     * @param userCode 车辆编号
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/consumePush")
    public void consumePush(String token, Integer type, String userCode, Long addTime, Long endTime, Long time,
                            HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            /* 校验 */
            Assert.hasLength(userCode, "-30006", "用户码不能为空");
            Assert.notNull(type, "-170010", "类型不能为空");
            Assert.notNull(addTime, "-170010", "类型不能为空");
            Assert.notNull(endTime, "-170010", "类型不能为空");
            Assert.notNull(time, "-170010", "类型不能为空");
            Machine machine = machineService.getByUserCode(userCode);
            if (machine == null) {
                throw new BaseException("-30005", "设备不存在");
            }
            User user = tokenService.getUser(token);
            String orderSource = request.getHeader("orderSource");
            String software = request.getHeader("software");
            String appWare = request.getHeader("appWare");
            String way = request.getHeader("way");
            String phoneType = request.getHeader("phoneType");
            Integer parkPointId = null;
            ParkMachine parkMachine = parkMachineService.getByMachineId(machine.getMachineId());
            if (parkMachine != null) {
                parkPointId = parkMachine.getParkPointId();
            }
            ComsumTime comsumTime = new ComsumTime(machine.getAccountId(), user.getUserId(), new Date(addTime)
                    , new Date(endTime), type, UseTimeConstant.USE_TIME_INTERNET, time.intValue(), machine.getMachineNO()
                    , machine.getUserCode(), parkPointId, phoneType, way, Integer.valueOf(orderSource), software, appWare);
            MqData mqData = new MqData();
            comsumTime.setWay("");
            mqData.setFeedback(RabbitMQData.aiQueue);
            mqData.setMsgId(GatewayProtocol.MQ_MSG_ID_AROUND_TIME_PUSH);
            mqData.setData(comsumTime);
            SystemData.pushList.add(mqData);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 馈电上报
     *
     * @param userCode 用户代码
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/addBattery")
    public void addBattery(String userCode, HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            /* 校验 */
			/*Assert.hasLength(userCode,"-30006", "用户码不能为空");
			if(SystemConstant.PUSH_BUTTON.equals("1")){
				Runnable runnable = new Runnable(){
					@Override
					public void run() {
						try {
							String url = "http://"+SystemConstant.PUSH_URL+":8085/machine/addBattery.do?userCode="+userCode;
							String result= HttpClientUtils.httpGet(url,null);
						}catch (Exception e){
							//log.error("上报异常",e);
						}

					}
				};
				runnable.run();
			}*/

            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 上报欲借车数
     *
     * @param userCode 车辆编号
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/addNum")
    public void addNum(String userCode, HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            /* 校验 *//*
			Assert.hasLength(userCode, "-30006", "用户码不能为空");
			if(SystemConstant.PUSH_BUTTON.equals("1")){
			Runnable runnable = new Runnable() {
				@Override
				public void run() {
					try {
						String url = "http://"+SystemConstant.PUSH_URL+":8085/machine/addNum.do?userCode=" + userCode;
						String result = HttpClientUtils.httpGet(url, null);
					} catch (Exception e) {
						//log.error("上报异常",e);
					}
				}
			};
			runnable.run();
		}*/
            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 统计是否满足头盔锁等条件
     *
     * @param token    令牌
     * @param lon      经度
     * @param lat      纬度
     * @param mapType  地图类型
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/checkOffLine")
    public void checkOffLine(String token, Double lon, Double lat, @RequestParam(defaultValue = "2") Integer mapType,
                             HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            /* 校验 */
            User user = tokenService.getUser(token);
            Point point = null;
            if (lon != null && lat != null) {
                point = new Point(lon, lat);
                point = calibrateService.calibrate(point, mapType, SysConstant.ORIGINAL);
            }
            machineService.isSuccess(user.getUserId(), point.getX(), point.getY(), mapType);

            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }
        OutputUtil.renderText(request, response, map);
    }

    /**
     * 主动上报道钉信息
     *
     * @param infos    道钉信息
     * @param userCode 车辆编号
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/addBleInfo")
    public void addBleInfo(String infos, String userCode, HttpServletRequest request,
                           HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            /* 校验 */
            Assert.hasLength(infos, "-30008", "道钉信息不能为空");
            Assert.hasLength(userCode, "-30006", "车辆编号不能为空");
            Machine machine = machineService.getByUserCode(userCode);
            if (machine == null) {
                throw new BaseException("-30005", "设备不存在");
            }
            List<BleNode> nodes = JSONObject.parseArray(infos, BleNode.class);
            BleInfo bleInfo = new BleInfo();
            bleInfo.setUpdateTime(new Date().getTime());
            bleInfo.setNodes(nodes);
            redisService.add(RedisConstant.MACHINE_NO_BLEINFO + machine.getMachineNO(), JSON.toJSONString(bleInfo), 60);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 是否支持头盔锁等
     *
     * @param userCode 车辆编号
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/isSupport")
    public void isSupport(String userCode, HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<>(8);
        try {
            /* 校验 */
            Assert.hasLength(userCode, "-30006", "车辆编号不能为空");
            List<MachineFunction> functions = machineService.isSupport(userCode);
            BleHelmet bleHelmet = bleHelmetService.getByUserCode(userCode);

            map.put("bleHelmet", bleHelmet);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, functions);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 获取扫码url对应key
     *
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/getQrCodeUrl")
    public void getQrCodeUrl(HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            /* 校验 */
            String qrcode = redisService.get(RedisConstant.QRCODE_URL);
            if (qrcode == null) {
                Properties props = new Properties();
                try {
                    props.load(new InputStreamReader(RabbitMQData.class.getClassLoader().getResourceAsStream("system.properties"), "UTF-8"));
                } catch (IOException e) {
                    log.error(e.getMessage(), e);
                }
                qrcode = props.getProperty("qrcodeUrl");
                redisService.add(RedisConstant.QRCODE_URL, qrcode, 300);
            }
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, qrcode);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 获取设备信息
     *
     * @param userCode 车辆编号
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/getMachineInfo")
    public void getMachineInfo(String userCode, HttpServletRequest request,
                               HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            Assert.hasLength(userCode, "-30006", "车辆编号不能为空");

            Machine machine = machineService.getByUserCode(userCode);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, machine);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 更新soc
     *
     * @param userCode 车辆编号
     * @param soc      电量
     * @param request
     * @param response
     */
    @RequestMapping("/editHelmet")
    public void editHelmet(String userCode, Integer soc, HttpServletRequest request,
                           HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            Assert.hasLength(userCode, "-30006", "车辆编号不能为空");

            Machine machine = machineService.getByUserCode(userCode);
            if (machine == null) {
                throw new BaseException("-30005", "");
            }
            bleHelmetService.edit(machine.getMachineId(), soc);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, machine);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 上传蓝牙头盔锁状态
     *
     * @param userCode 车辆编号
     * @param status   状态
     * @param type     类型 0借车，1还车
     * @param request
     * @param response
     */
    @RequestMapping("/helmetInfo")
    public void helmetInfo(String userCode, Integer status, @RequestParam(defaultValue = "1") Integer type, HttpServletRequest request,
                           HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            Assert.hasLength(userCode, "-30006", "车辆编号不能为空");
            Machine machine = machineService.getByUserCode(userCode);
            if (machine == null) {
                throw new BaseException("-30005", "");
            }
            log.info("车辆编号" + userCode + "上传头盔状态" + status + "类型=" + type);
            if (type == 1) {
                redisService.add(FunctionConstant.CARSTATUS + machine.getMachineNO(), status.toString(), 20);
            } else {
                redisService.add(RedisConstant.RIDELOG_BEFORE_BORROW_STATUS + machine.getMachineNO(), status.toString(), 12 * 3600);
            }
            MachineBorrow machineBorrow = machineBorrowService.getByMachineId(machine.getMachineId());
            if (machineBorrow != null) {
                log.info("订单" + machineBorrow.getOrderNO() + "上传头盔状态" + status);
            }
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, machine);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 超时结束订单、人工结束订单
     *
     * @param userId        用户id
     * @param money         金额
     * @param remark        备注
     * @param dispatchType  调度金额
     * @param type          类型 为2则是人工结束订单，3是超时结束订单
     * @param sign          签名
     * @param date          时间戳
     * @param param         附加参数
     * @param operatorId    操作人id
     * @param operatorName  操作人名称
     * @param operatorPhone 操作人手机号码
     * @param secondType    第二调度类型，5=头盔未归还
     * @param request
     * @param response
     */
    @RequestMapping("/finishOrder")
    public void finishOrder(Integer userId, Integer money, String remark, Integer dispatchType, Integer type, Integer secondType
            , String sign, Long date, String param, Integer operatorId, String operatorName, String operatorPhone, HttpServletRequest request,
                            HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            Assert.notNull(userId, "-40002", "用户id不能为空");
            Assert.notNull(type, "-40003", "结束类型不能为空");
            /**并发限制，5秒内不许重复结束订单*/
            if (!redisService.setNx("finishOrder." + userId, "1", 5)) {
                throw new BaseException("-102", "操作频繁，请稍后再试");
            }
            /**type 为2则是人工结束订单，3是超时结束订单*/
            if (type == 2) {
                Assert.notNull(money, "-40004", "扣费金额不能为空");
            } else {
                Assert.notNull(dispatchType, "-40005", "调度类型不能为空");
            }

            boolean result = MD5PasswordUtil.signCheck(sign, date, param);
            if (!result) {
                throw new BaseException("-190002", "签名校验失败");
            }

            EmojiCheckUtil.check(remark);
            Integer lessMoney = finishOrderService.finishOrder(userId, money, remark, dispatchType, type, operatorId, operatorName, operatorPhone, secondType);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, lessMoney);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 判断用户是否超远距离借车/运营区外借车
     * @param borrowUserInfoDTO
     */
    @GetMapping("/validPopUp")
    public void validUserBorrow(BorrowUserInfoDTO borrowUserInfoDTO,HttpServletRequest request,
                                HttpServletResponse response){
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            Machine machine = machineService.getByUserCode(borrowUserInfoDTO.getUserCode());
            AccountConfigExt accountConfigExt = cacheService.getAccountConfigExtByKey(machine.getAccountId(),  AccountConfigExtConstant.POWER_tIME);
            AccountConfig accountConfig = accountConfigService.getByAccountId(machine.getAccountId());
            boolean tooFar = machineBorrowService.validTooFar(borrowUserInfoDTO,machine);
            int parkTime = 0;
            if (tooFar) {
                parkTime = Objects.isNull(accountConfig) ? 30 : accountConfig.getFinishTime();
            }
            boolean outOfRange = machineBorrowService.validOutOfRange(borrowUserInfoDTO,machine);
            int powerTime = 0;
            if (outOfRange) {
                powerTime = Objects.isNull(accountConfigExt) ? 10 : Integer.parseInt(accountConfigExt.getParamValue());
            }
            BorrowPopUp borrowPopUp = new BorrowPopUp(tooFar,parkTime,outOfRange,powerTime);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, borrowPopUp);
        } catch (BaseException e) {
        ResponseCode.printException(map, e, request);
        }
        OutputUtil.renderText(request, response, map);
    }

}